package se.thoka.twt.client.utils;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.i18n.shared.DateTimeFormatInfo;

/**
 * @author Thobias Karlsson, Thoka
 * @since 9 jan 2010
 */
public class I18NHandler {
	/**
	 * The configuration settings
	 */
	public interface Config extends Constants {
		/**
		 * @return The overridden date and time format, or <code>null</code> if default should be used
		 */
		@DefaultStringValue("yyyy-MM-dd HH:mm")
		String dateTimeFormatOverride();
		/**
		 * @return The overridden date format, or <code>null</code> if default should be used
		 */
		@DefaultStringValue("yyyy-MM-dd")
		String dateFormatOverride();
		/**
		 * @return The overridden time format, or <code>null</code> if default should be used
		 */
		@DefaultStringValue("HH:mm")
		String timeFormatOverride();
	}
	
	/**
	 * The internationalization settings
	 */
	public interface Strings extends Constants {
		/**
		 * @return The week initial, e.g. "w" for "week"
		 */
		@DefaultStringValue(value = "w")
		String weekInitial();
		/**
		 * @return The translation of today
		 */
		@DefaultStringValue(value = "today")
		String today();
		/**
		 * @return The translation of tomorrow
		 */
		@DefaultStringValue(value = "tomorrow")
		String tomorrow();
		/**
		 * @return The translation of yesterday
		 */
		@DefaultStringValue(value = "yesterday")
		String yesterDay();
	}
	
	private Config config;
	private Strings strings;
	
	private final int firstDayOfWeek;
	private final DateTimeFormat dateTimeFormat, dateFormat, timeFormat;
	
	/**
	 * Creates a new instance of {@link I18NHandler}
	 */
	public I18NHandler() {
		
		Config config = getConfig();
		
		// First day of week
		int firstDayOfWeek = getConstants().firstDayOfTheWeek();
		if (firstDayOfWeek >= 0 && firstDayOfWeek < 7) {
			this.firstDayOfWeek = firstDayOfWeek;
		} else {
			TWTLogger.log(TWTLogLevel.ERROR, "First day of week (" + firstDayOfWeek + ") must be an integer between 0...6, defaulting to Sunday");
			this.firstDayOfWeek = DateTimeUtil.SUNDAY;
		}
		
		// Date and time formats
		dateTimeFormat = getFormat(DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM), 
				config.dateTimeFormatOverride(), "DateTime");
		timeFormat = getFormat(DateTimeFormat.getFormat(PredefinedFormat.TIME_SHORT),
				config.timeFormatOverride(), "Time");
		dateFormat = getFormat(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT),
				config.dateFormatOverride(), "Date");
	}

	/**
	 * @return The date and time format
	 */
	public DateTimeFormat getDateTimeFormat() {
		return dateTimeFormat;
	}
	
	/**
	 * @return The date format
	 */
	public DateTimeFormat getDateFormat() {
		return dateFormat;
	}
	
	/**
	 * @return The time format
	 */
	public DateTimeFormat getTimeFormat() {
		return timeFormat;
	}
	
	/**
	 * @return The start day of week, where <code>0</code> is Sunday
	 */
	public int getStartOfWeek() {
		return firstDayOfWeek;
	}

	/**
	 * Returns the month name of the supplied day offset
	 * 
	 * @param monthOffset The month offset, where <code>1</code> is January
	 * @return The month name
	 */
	public String getMonthName(int monthOffset) {
		return getConstants().monthsFull()[monthOffset];
	}

	/**
	 * Returns the short month name of the supplied month offset
	 * 
	 * @param monthOffset The month offset, where <code>1</code> is January
	 * @return The short month name
	 */
	public String getMonthShortName(int monthOffset) {
		return getConstants().monthsShort()[monthOffset];
	}

	/**
	 * Returns the week day initial of the supplied day offset
	 * 
	 * @param dayOffset The day offset, where <code>0</code> is Sunday
	 * @return The week day initial
	 */
	public String getWeekDayInitial(int dayOffset) {
		return getConstants().weekdaysNarrow()[dayOffset];
	}
	
	/**
	 * Returns the short week day name of the supplied day offset
	 * 
	 * @param dayOffset The day offset, where <code>0</code> is Sunday
	 * @return The short week day name
	 */
	public String getWeekDayShortName(int dayOffset) {
		return getConstants().weekdaysShort()[dayOffset];
	}
	
	/**
	 * Returns the week day name of the supplied date
	 * 
	 * @param dayOffset The day offset, where <code>0</code> is Sunday
	 * @return The week day name
	 */
	public String getWeekDayName(int dayOffset) {
		return getConstants().weekdaysFull()[dayOffset];
	}
	
	/**
	 * @return The week initial, e.g. "w" for "week"
	 */
	public String getWeekInitial() {
		return getStrings().weekInitial();
	}
	
	/**
	 * @return The translation of "Today"
	 */
	public String getTodayTranslation() {
		return getStrings().today();
	}
	
	/**
	 * @return The translation of "Tomorrow"
	 */
	public String getTomorrowTranslation() {
		return getStrings().tomorrow();
	}
	
	/**
	 * @return The translation of "Yesterday"
	 */
	public String getYesterdayTranslation() {
		return getStrings().yesterDay();
	}
	
	/**
	 * @return The configuration settings
	 */
	protected Config getConfig() {
		if (config == null) {
			config = GWT.create(Config.class);
		}
		
		return config;
	}
	
	/**
	 * @return The internationalization settings
	 */
	protected Strings getStrings() {
		if (strings == null) {
			strings = GWT.create(Strings.class);
		}
		
		return strings;
	}
	
	/**
	 * @return The GWT date time constants
	 */
	protected DateTimeFormatInfo getConstants() {
		return LocaleInfo.getCurrentLocale().getDateTimeFormatInfo();
	}
	
	private DateTimeFormat getFormat(DateTimeFormat defaultFormat, String overrideFormat, String dateFormatType) {
		DateTimeFormat result;
		
		DateTimeFormat override = !StringUtils.nullOrEmpty(overrideFormat) ? DateTimeFormat.getFormat(overrideFormat) : null;
		if (override != null) {
			TWTLogger.log(TWTLogLevel.INFO, dateFormatType + " format overridden with \"" + override + "\"");
			result = override;
		} else {
			result = defaultFormat;
		}
		
		return result;
	}
}
